package main

import (
	"errors"
	"strconv"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)

//TCondition the mongo filter
type TCondition struct {
	Key   string
	Opt   string
	Type  string
	Value string
}

//TPaging split page model
type TPaging struct {
	Skip      *int64
	Limit     *int64
	SortKey   string
	SortVal   int
	Condition *bson.D
}

//MakeFilterBSOND make the filter for bson.D by condition
func MakeFilterBSOND(condition ...TCondition) (*bson.D, error) {
	idx, want := bsoncore.AppendDocumentStart(nil)
	for _, c := range condition {
		if c.Value == "" {
			continue
		}
		if c.Key == "" {
			continue
		}
		if c.Type == "" {
			continue
		}
		if c.Opt != "$eq" && c.Opt != "$like" && c.Opt != "$lt" && c.Opt != "$lte" && c.Opt != "$gt" && c.Opt != "$gte" && c.Opt != "$ne" {
			return nil, errors.New("the Opt not support")
		}
		switch c.Type {
		case "string":
			switch c.Opt {
			case "$like":
				want = bsoncore.AppendRegexElement(want, c.Key, c.Value, "")
			case "$eq":
				want = bsoncore.AppendStringElement(want, c.Key, c.Value)
			default:
				want = bsoncore.AppendDocument(want, bsoncore.BuildDocumentElement(nil, c.Key, bsoncore.AppendStringElement(nil, c.Opt, c.Value)))
			}
		case "int", "int32":
			val, err := strconv.ParseInt(c.Value, 10, 32)
			if err != nil {
				return nil, err
			}
			if c.Opt == "$eq" {
				want = bsoncore.AppendInt32Element(want, c.Opt, int32(val))
			} else {
				want = bsoncore.AppendDocument(want, bsoncore.BuildDocumentElement(nil, c.Key, bsoncore.AppendInt32Element(nil, c.Opt, int32(val))))
			}
		case "int64":
			val, err := strconv.ParseInt(c.Value, 10, 64)
			if err != nil {
				return nil, err
			}
			if c.Opt == "$eq" {
				want = bsoncore.AppendInt64Element(nil, c.Opt, val)
			} else {
				want = bsoncore.AppendDocument(want, bsoncore.BuildDocumentElement(nil, c.Key, bsoncore.AppendInt64Element(nil, c.Opt, val)))
			}

		case "float":
			val, err := strconv.ParseFloat(c.Value, 64)
			if err != nil {
				return nil, err
			}
			if c.Opt == "$eq" {
				want = bsoncore.AppendDoubleElement(nil, c.Opt, val)
			} else {
				want = bsoncore.AppendDocument(want, bsoncore.BuildDocumentElement(nil, c.Key, bsoncore.AppendDoubleElement(nil, c.Opt, val)))
			}

		case "date":
			loc, _ := time.LoadLocation("Asia/Shanghai")
			const timeTemplate = "2006-01-02 15:04:05"
			val, err := time.ParseInLocation(timeTemplate, c.Value, loc)
			if err != nil {
				return nil, err
			}
			if c.Opt == "$eq" {
				want = bsoncore.AppendDateTimeElement(nil, c.Opt, val.UnixNano()/int64(time.Millisecond))
			} else {
				want = bsoncore.AppendDocument(want, bsoncore.BuildDocumentElement(nil, "date", bsoncore.AppendDateTimeElement(nil, c.Opt, val.UnixNano()/int64(time.Millisecond))))
			}

		case "boolean":
			val, err := strconv.ParseBool(c.Value)
			if err != nil {
				return nil, err
			}
			if c.Opt == "$eq" {
				want = bsoncore.AppendBooleanElement(nil, c.Opt, val)
			} else {
				want = bsoncore.AppendDocument(want, bsoncore.BuildDocumentElement(nil, "date", bsoncore.AppendBooleanElement(nil, c.Opt, val)))
			}

		}
	}
	want, err := bsoncore.AppendDocumentEnd(want, idx)
	if len(want) < 1 {
		return &bson.D{}, err
	}
	if err != nil {
		return nil, err
	}
	var got bson.D
	err = bson.Unmarshal(want, &got)
	if err != nil {
		return nil, err
	}
	return &got, nil
}
